Цель исследования - провести оценку результатов A/B-теста.
recommender_system_test;product_page,product_cart,purchase. import pandas as pd
import numpy as np
import math as mth
from scipy import stats as st
import seaborn as sns
import matplotlib.pyplot as plt
from plotly import graph_objects as go
from datetime import datetime
import warnings
warnings.filterwarnings('ignore')
sns.set_style("darkgrid")
try:
marketing_events = pd.read_csv('Datasets/ab_project_marketing_events.csv')
except:
marketing_events = pd.read_csv('https://code.s3.yandex.net/datasets/ab_project_marketing_events.csv')
try:
ab_events = pd.read_csv('Datasets/final_ab_events.csv')
except:
ab_events = pd.read_csv('https://code.s3.yandex.net/datasets/final_ab_events.csv')
try:
new_users = pd.read_csv('Datasets/final_ab_new_users.csv')
except:
new_users = pd.read_csv('https://code.s3.yandex.net/datasets/final_ab_new_users.csv')
try:
participants = pd.read_csv('Datasets/final_ab_participants.csv')
except:
participants = pd.read_csv('https://code.s3.yandex.net/datasets/final_ab_participants.csv')
marketing_events.head()
| name | regions | start_dt | finish_dt | |
|---|---|---|---|---|
| 0 | Christmas&New Year Promo | EU, N.America | 2020-12-25 | 2021-01-03 |
| 1 | St. Valentine's Day Giveaway | EU, CIS, APAC, N.America | 2020-02-14 | 2020-02-16 |
| 2 | St. Patric's Day Promo | EU, N.America | 2020-03-17 | 2020-03-19 |
| 3 | Easter Promo | EU, CIS, APAC, N.America | 2020-04-12 | 2020-04-19 |
| 4 | 4th of July Promo | N.America | 2020-07-04 | 2020-07-11 |
ab_project_marketing_events.csv — календарь маркетинговых событий на 2020 год.
name — название маркетингового события;regions — регионы, в которых будет проводиться рекламная кампания;start_dt — дата начала кампании;finish_dt — дата завершения кампании.ab_events.head()
| user_id | event_dt | event_name | details | |
|---|---|---|---|---|
| 0 | E1BDDCE0DAFA2679 | 2020-12-07 20:22:03 | purchase | 99.99 |
| 1 | 7B6452F081F49504 | 2020-12-07 09:22:53 | purchase | 9.99 |
| 2 | 9CD9F34546DF254C | 2020-12-07 12:59:29 | purchase | 4.99 |
| 3 | 96F27A054B191457 | 2020-12-07 04:02:40 | purchase | 4.99 |
| 4 | 1FD7660FDF94CA1F | 2020-12-07 10:15:09 | purchase | 4.99 |
final_ab_events.csv — действия новых пользователей в период с 7 декабря 2020 по 4 января 2021 года.
user_id — идентификатор пользователя;event_dt — дата и время события;event_name — тип события;details — дополнительные данные о событии. Например, для покупок, purchase, в этом поле хранится стоимость покупки в долларах.new_users.head()
| user_id | first_date | region | device | |
|---|---|---|---|---|
| 0 | D72A72121175D8BE | 2020-12-07 | EU | PC |
| 1 | F1C668619DFE6E65 | 2020-12-07 | N.America | Android |
| 2 | 2E1BF1D4C37EA01F | 2020-12-07 | EU | PC |
| 3 | 50734A22C0C63768 | 2020-12-07 | EU | iPhone |
| 4 | E1BDDCE0DAFA2679 | 2020-12-07 | N.America | iPhone |
final_ab_new_users.csv — пользователи, зарегистрировавшиеся с 7 по 21 декабря 2020 года.
user_id — идентификатор пользователя;first_date — дата регистрации;region — регион пользователя;device — устройство, с которого происходила регистрация.participants.head()
| user_id | group | ab_test | |
|---|---|---|---|
| 0 | D1ABA3E2887B6A73 | A | recommender_system_test |
| 1 | A7A3664BD6242119 | A | recommender_system_test |
| 2 | DABC14FDDFADD29E | A | recommender_system_test |
| 3 | 04988C5DF189632E | A | recommender_system_test |
| 4 | 482F14783456D21B | B | recommender_system_test |
final_ab_participants.csv — таблица участников тестов.
user_id — идентификатор пользователя;ab_test — название теста;group — группа пользователя.Перед началом исследовательского анализа необходимо проверить данные на наличие пропусков и дубликатов, а также на корректность форматов данных.
def date (df, col):
for c in col:
df[c] = pd.to_datetime(df[c])
marketing_events.info()
<class 'pandas.core.frame.DataFrame'> RangeIndex: 14 entries, 0 to 13 Data columns (total 4 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 name 14 non-null object 1 regions 14 non-null object 2 start_dt 14 non-null object 3 finish_dt 14 non-null object dtypes: object(4) memory usage: 576.0+ bytes
date(marketing_events, ['start_dt', 'finish_dt'])
marketing_events.info()
<class 'pandas.core.frame.DataFrame'> RangeIndex: 14 entries, 0 to 13 Data columns (total 4 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 name 14 non-null object 1 regions 14 non-null object 2 start_dt 14 non-null datetime64[ns] 3 finish_dt 14 non-null datetime64[ns] dtypes: datetime64[ns](2), object(2) memory usage: 576.0+ bytes
marketing_events
| name | regions | start_dt | finish_dt | |
|---|---|---|---|---|
| 0 | Christmas&New Year Promo | EU, N.America | 2020-12-25 | 2021-01-03 |
| 1 | St. Valentine's Day Giveaway | EU, CIS, APAC, N.America | 2020-02-14 | 2020-02-16 |
| 2 | St. Patric's Day Promo | EU, N.America | 2020-03-17 | 2020-03-19 |
| 3 | Easter Promo | EU, CIS, APAC, N.America | 2020-04-12 | 2020-04-19 |
| 4 | 4th of July Promo | N.America | 2020-07-04 | 2020-07-11 |
| 5 | Black Friday Ads Campaign | EU, CIS, APAC, N.America | 2020-11-26 | 2020-12-01 |
| 6 | Chinese New Year Promo | APAC | 2020-01-25 | 2020-02-07 |
| 7 | Labor day (May 1st) Ads Campaign | EU, CIS, APAC | 2020-05-01 | 2020-05-03 |
| 8 | International Women's Day Promo | EU, CIS, APAC | 2020-03-08 | 2020-03-10 |
| 9 | Victory Day CIS (May 9th) Event | CIS | 2020-05-09 | 2020-05-11 |
| 10 | CIS New Year Gift Lottery | CIS | 2020-12-30 | 2021-01-07 |
| 11 | Dragon Boat Festival Giveaway | APAC | 2020-06-25 | 2020-07-01 |
| 12 | Single's Day Gift Promo | APAC | 2020-11-11 | 2020-11-12 |
| 13 | Chinese Moon Festival | APAC | 2020-10-01 | 2020-10-07 |
Пропущенные значения: Отсутствуют
Дубликаты: Отсутствуют
Форматы данных: Даты приведены к формату datetime
ab_events.info()
<class 'pandas.core.frame.DataFrame'> RangeIndex: 440317 entries, 0 to 440316 Data columns (total 4 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 user_id 440317 non-null object 1 event_dt 440317 non-null object 2 event_name 440317 non-null object 3 details 62740 non-null float64 dtypes: float64(1), object(3) memory usage: 13.4+ MB
date(ab_events, ['event_dt'])
ab_events.head()
| user_id | event_dt | event_name | details | |
|---|---|---|---|---|
| 0 | E1BDDCE0DAFA2679 | 2020-12-07 20:22:03 | purchase | 99.99 |
| 1 | 7B6452F081F49504 | 2020-12-07 09:22:53 | purchase | 9.99 |
| 2 | 9CD9F34546DF254C | 2020-12-07 12:59:29 | purchase | 4.99 |
| 3 | 96F27A054B191457 | 2020-12-07 04:02:40 | purchase | 4.99 |
| 4 | 1FD7660FDF94CA1F | 2020-12-07 10:15:09 | purchase | 4.99 |
ab_events[ab_events['event_name'] == "purchase"].info()
<class 'pandas.core.frame.DataFrame'> Int64Index: 62740 entries, 0 to 62739 Data columns (total 4 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 user_id 62740 non-null object 1 event_dt 62740 non-null datetime64[ns] 2 event_name 62740 non-null object 3 details 62740 non-null float64 dtypes: datetime64[ns](1), float64(1), object(2) memory usage: 2.4+ MB
ab_events['event_name'].unique()
array(['purchase', 'product_cart', 'product_page', 'login'], dtype=object)
Пропущенные значения: В поле details, где event_name != purchase, заполнение не требуется
Дубликаты: Отсутствуют
Форматы данных: Даты приведены к формату datetime
new_users.info()
<class 'pandas.core.frame.DataFrame'> RangeIndex: 61733 entries, 0 to 61732 Data columns (total 4 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 user_id 61733 non-null object 1 first_date 61733 non-null object 2 region 61733 non-null object 3 device 61733 non-null object dtypes: object(4) memory usage: 1.9+ MB
date(new_users, ['first_date'])
new_users.head()
| user_id | first_date | region | device | |
|---|---|---|---|---|
| 0 | D72A72121175D8BE | 2020-12-07 | EU | PC |
| 1 | F1C668619DFE6E65 | 2020-12-07 | N.America | Android |
| 2 | 2E1BF1D4C37EA01F | 2020-12-07 | EU | PC |
| 3 | 50734A22C0C63768 | 2020-12-07 | EU | iPhone |
| 4 | E1BDDCE0DAFA2679 | 2020-12-07 | N.America | iPhone |
sum(new_users['user_id'].duplicated())
0
new_users['region'].unique()
array(['EU', 'N.America', 'APAC', 'CIS'], dtype=object)
new_users['device'].unique()
array(['PC', 'Android', 'iPhone', 'Mac'], dtype=object)
Пропущенные значения: Отсутствуют
Дубликаты: Отсутствуют
Форматы данных: Даты приведены к формату datetime
participants.info()
<class 'pandas.core.frame.DataFrame'> RangeIndex: 18268 entries, 0 to 18267 Data columns (total 3 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 user_id 18268 non-null object 1 group 18268 non-null object 2 ab_test 18268 non-null object dtypes: object(3) memory usage: 428.3+ KB
participants.head()
| user_id | group | ab_test | |
|---|---|---|---|
| 0 | D1ABA3E2887B6A73 | A | recommender_system_test |
| 1 | A7A3664BD6242119 | A | recommender_system_test |
| 2 | DABC14FDDFADD29E | A | recommender_system_test |
| 3 | 04988C5DF189632E | A | recommender_system_test |
| 4 | 482F14783456D21B | B | recommender_system_test |
sum(participants.duplicated())
0
sum(participants['user_id'].duplicated())
1602
participants['group'].unique()
array(['A', 'B'], dtype=object)
participants['ab_test'].unique()
array(['recommender_system_test', 'interface_eu_test'], dtype=object)
Пропущенные значения: Отсутствуют
Дубликаты: Некоторые значения user_id повторяются, следовательно некоторые пользователи участвуют одновременно в нескольких тестах или нескольких группах
Форматы данных: Корректны
participants = participants.merge(new_users, on='user_id')
rec_participants = participants.query('ab_test == "recommender_system_test"')
ab_events = ab_events.query('user_id in @rec_participants["user_id"]')
ab_events['event_dt'].min()
Timestamp('2020-12-07 00:05:57')
ab_events['event_dt'].max()
Timestamp('2020-12-30 12:42:57')
Дата запуска соответствует заявленной,
Дата остановки на 5 дней раньше запланированной
Также возможно что пользователи не совершали действий в период с 31.12.2020 по 04.01.2021
new_users.query('user_id in @rec_participants["user_id"]')['first_date'].min()
Timestamp('2020-12-07 00:00:00')
new_users.query('user_id in @rec_participants["user_id"]')['first_date'].max()
Timestamp('2020-12-21 00:00:00')
Дата запуска и остановки набора новых пользователей соответствуют заявленным.
new_users = new_users.query('first_date <= "2020-12-22"')
new_users['first_date'].max()
Timestamp('2020-12-22 00:00:00')
Пользователи отфильтрованы по датам набора в соответствии с ТЗ.
rec_participants['region'].unique()
array(['EU', 'N.America', 'CIS', 'APAC'], dtype=object)
rec_participants = rec_participants.query('region == "EU"')
Некоторые участники теста не принадлежат к региону EU, что не соответствует ТЗ.
Данные отфильтрованы
len(rec_participants)
6351
new_eu_users = new_users.query('region == "EU"')
new_relevant_eu_users = new_eu_users.query('first_date >= "2020-12-07"')
new_relevant_eu_users = new_relevant_eu_users.query('first_date <= "2020-12-21"')
len(new_eu_users)
44632
round(len(rec_participants) / len(new_relevant_eu_users), 3)
0.15
Количество участников теста соответствует ТЗ:
6351 пользователь
15% от новых пользователей региона EU
ab_events['event_dt'].min()
Timestamp('2020-12-07 00:05:57')
ab_events['event_dt'].max()
Timestamp('2020-12-30 12:42:57')
marketing_events
| name | regions | start_dt | finish_dt | |
|---|---|---|---|---|
| 0 | Christmas&New Year Promo | EU, N.America | 2020-12-25 | 2021-01-03 |
| 1 | St. Valentine's Day Giveaway | EU, CIS, APAC, N.America | 2020-02-14 | 2020-02-16 |
| 2 | St. Patric's Day Promo | EU, N.America | 2020-03-17 | 2020-03-19 |
| 3 | Easter Promo | EU, CIS, APAC, N.America | 2020-04-12 | 2020-04-19 |
| 4 | 4th of July Promo | N.America | 2020-07-04 | 2020-07-11 |
| 5 | Black Friday Ads Campaign | EU, CIS, APAC, N.America | 2020-11-26 | 2020-12-01 |
| 6 | Chinese New Year Promo | APAC | 2020-01-25 | 2020-02-07 |
| 7 | Labor day (May 1st) Ads Campaign | EU, CIS, APAC | 2020-05-01 | 2020-05-03 |
| 8 | International Women's Day Promo | EU, CIS, APAC | 2020-03-08 | 2020-03-10 |
| 9 | Victory Day CIS (May 9th) Event | CIS | 2020-05-09 | 2020-05-11 |
| 10 | CIS New Year Gift Lottery | CIS | 2020-12-30 | 2021-01-07 |
| 11 | Dragon Boat Festival Giveaway | APAC | 2020-06-25 | 2020-07-01 |
| 12 | Single's Day Gift Promo | APAC | 2020-11-11 | 2020-11-12 |
| 13 | Chinese Moon Festival | APAC | 2020-10-01 | 2020-10-07 |
Даты проведения эксперимента частично пересекаются с маркетинговым событием Christmas & New Year Promo.
Необходимо будет учитывать это при изучении абсолютных значений, поскольку набор статистической значимости эксперимента может распределяться неравномерно.
participants_pivot = participants.groupby('user_id').agg('nunique')[['group', 'ab_test']]
participants_pivot
| group | ab_test | |
|---|---|---|
| user_id | ||
| 0002CE61FF2C4011 | 1 | 1 |
| 000ABE35EE11412F | 1 | 1 |
| 001064FEAAB631A1 | 1 | 2 |
| 0010A1C096941592 | 1 | 1 |
| 001C05E87D336C59 | 1 | 1 |
| ... | ... | ... |
| FFE858A7845F005E | 1 | 1 |
| FFED90241D04503F | 1 | 2 |
| FFEFC0E55C1CCD4F | 1 | 1 |
| FFF28D02B1EACBE1 | 2 | 2 |
| FFF58BC33966EB51 | 1 | 1 |
16666 rows × 2 columns
Как было обнаружено ранее, часть пользователей принимают участие одновременно в нескольких тестах или находятся одновременно в обеих исследуемых группах.
participants_pivot_filtered = participants_pivot.query('group == 1')
participants_pivot_filtered = participants_pivot_filtered.query('ab_test == 1')
participants_filtered = rec_participants.query('user_id in @participants_pivot_filtered.index')
Также, поскольку регистрация событий пользователей была завершена ранее запланированной даты, необходимо исключить пользователей, лайфтайм которых оказался меньше "горизонта событий" (14 дней) чтобы убедиться в том, что у пользователей было достаточно времени на совершение всех действий.
participants_filtered = participants_filtered.query('first_date <= "2020-12-16"')
participants_filtered['user_id'].nunique()
3035
При этом количество пользователей оказалось на 50% меньше заявленного.
participants_filtered.query('group == "A"')['user_id'].nunique()
1743
participants_filtered.query('group == "B"')['user_id'].nunique()
1292
Распределение пользователей по группам неравномерное.
Следовательно, при дальнейшем анализе необходимо оперировать относительными показателями.
participants_filtered.groupby('device')['user_id'].agg('nunique').sort_values().index
Index(['Mac', 'iPhone', 'PC', 'Android'], dtype='object', name='device')
plt.figure(figsize=(10, 5))
sns.countplot(data = participants_filtered,
x = 'device',
hue = 'group',
order = participants_filtered.groupby('device')['user_id'].agg('nunique').sort_values().index,
)
plt.title('Распределение пользователей по устройствам')
plt.ylabel('Количество пользователей')
plt.xlabel('Используемое устройство');
Распределение пользователей по типу используемых устройств идентчно.
При наборе пользователей и проведении теста замечены некоторые неточности:
ab_events_filtered = ab_events.query('user_id in @participants_filtered["user_id"]')
ab_events_filtered = ab_events_filtered.merge(participants, on='user_id')
user_events_count = ab_events_filtered.pivot_table(index = ['user_id', 'group'],
values = 'event_dt',
aggfunc = 'count').reset_index()
plt.figure(figsize=(10, 5))
sns.countplot(data = user_events_count,
x = 'event_dt',
hue='group'
)
plt.title('Распределение количества событий, совершаемых пользователем')
plt.ylabel('Количество пользователей')
plt.xlabel('Количество событий');
round(user_events_count.mean(), 2)
event_dt 6.89 dtype: float64
round(user_events_count.query('group == "A"').mean(), 2)
event_dt 7.24 dtype: float64
round(user_events_count.query('group == "B"').mean(), 2)
event_dt 6.15 dtype: float64
Распределение количества событий, совершаемых пользователем близко к нормальному в обеих группах.
В группе B среднее количество событий ниже, чем в контрольной.
ab_events_filtered['event_date'] = pd.to_datetime(ab_events_filtered['event_dt']).dt.date
ab_events_filtered
| user_id | event_dt | event_name | details | group | ab_test | first_date | region | device | event_date | |
|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 831887FE7F2D6CBA | 2020-12-07 06:50:29 | purchase | 4.99 | A | recommender_system_test | 2020-12-07 | EU | Android | 2020-12-07 |
| 1 | 831887FE7F2D6CBA | 2020-12-09 02:19:17 | purchase | 99.99 | A | recommender_system_test | 2020-12-07 | EU | Android | 2020-12-09 |
| 2 | 831887FE7F2D6CBA | 2020-12-07 06:50:30 | product_cart | NaN | A | recommender_system_test | 2020-12-07 | EU | Android | 2020-12-07 |
| 3 | 831887FE7F2D6CBA | 2020-12-08 10:52:27 | product_cart | NaN | A | recommender_system_test | 2020-12-07 | EU | Android | 2020-12-08 |
| 4 | 831887FE7F2D6CBA | 2020-12-09 02:19:17 | product_cart | NaN | A | recommender_system_test | 2020-12-07 | EU | Android | 2020-12-09 |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 9775 | 60A008C4D5A3B8F2 | 2020-12-18 22:01:54 | login | NaN | A | recommender_system_test | 2020-12-16 | EU | Android | 2020-12-18 |
| 9776 | 60A008C4D5A3B8F2 | 2020-12-21 08:57:33 | login | NaN | A | recommender_system_test | 2020-12-16 | EU | Android | 2020-12-21 |
| 9777 | 4266741E592070B6 | 2020-12-16 07:05:00 | login | NaN | A | recommender_system_test | 2020-12-16 | EU | iPhone | 2020-12-16 |
| 9778 | 4266741E592070B6 | 2020-12-17 22:35:21 | login | NaN | A | recommender_system_test | 2020-12-16 | EU | iPhone | 2020-12-17 |
| 9779 | 4266741E592070B6 | 2020-12-19 06:57:40 | login | NaN | A | recommender_system_test | 2020-12-16 | EU | iPhone | 2020-12-19 |
9780 rows × 10 columns
a_date_events_count = ab_events_filtered.query('group == "A"'
).pivot_table(index = 'event_date',
values = 'user_id',
aggfunc = 'count')
a_date_events_count.columns = ['a_events']
b_date_events_count = ab_events_filtered.query('group == "B"'
).pivot_table(index = 'event_date',
values = 'user_id',
aggfunc = 'count')
b_date_events_count.columns = ['b_events']
date_events_count = a_date_events_count.copy()
date_events_count['b_events'] = b_date_events_count
date_events_count
| a_events | b_events | |
|---|---|---|
| event_date | ||
| 2020-12-07 | 221 | 268 |
| 2020-12-08 | 239 | 169 |
| 2020-12-09 | 282 | 250 |
| 2020-12-10 | 246 | 182 |
| 2020-12-11 | 264 | 115 |
| 2020-12-12 | 273 | 150 |
| 2020-12-13 | 233 | 103 |
| 2020-12-14 | 755 | 193 |
| 2020-12-15 | 780 | 164 |
| 2020-12-16 | 748 | 275 |
| 2020-12-17 | 532 | 134 |
| 2020-12-18 | 384 | 101 |
| 2020-12-19 | 340 | 101 |
| 2020-12-20 | 248 | 83 |
| 2020-12-21 | 240 | 77 |
| 2020-12-22 | 216 | 44 |
| 2020-12-23 | 176 | 82 |
| 2020-12-24 | 160 | 62 |
| 2020-12-25 | 163 | 47 |
| 2020-12-26 | 137 | 45 |
| 2020-12-27 | 142 | 54 |
| 2020-12-28 | 113 | 38 |
| 2020-12-29 | 119 | 30 |
plt.figure(figsize=(10, 5))
sns.lineplot(y=date_events_count['a_events'],
x=date_events_count.index
)
sns.lineplot(y=date_events_count['b_events'],
x=date_events_count.index
)
plt.title('Количество событий совершаемых пользователями ежедневно')
plt.ylabel('Количество событий')
plt.xlabel('Дата');
round(date_events_count['a_events'].mean(), 2)
304.83
round(date_events_count['b_events'].mean(), 2)
120.3
Количество событий, совершаемых пользователями ежедневно имеет значительный разброс. Также заметно резкое повышение спроса, связанного с сезонностью.
При этом повышение количества действий наблюдается только в контрольной группе.
Количество событий в группе B имеет незначительный рост в данный период.
В связи с этим среднее количество событий в группе B заметно ниже, чем в контрольной.
a_whirpool = ab_events_filtered.query('group == "A"').pivot_table(index = 'event_name',
values = 'user_id',
aggfunc = 'nunique').sort_values(by='user_id',
ascending=False)
a_whirpool.columns = ['a_users_count']
a_whirpool
| a_users_count | |
|---|---|
| event_name | |
| login | 969 |
| product_page | 623 |
| product_cart | 300 |
| purchase | 286 |
fig = go.Figure(go.Funnel(y=a_whirpool.index,
x=a_whirpool['a_users_count']
)
)
fig.update_layout(title="График воронки событий контрольной группы")
fig.show()
b_whirpool = ab_events_filtered.query('group == "B"').pivot_table(index = 'event_name',
values = 'user_id',
aggfunc = 'nunique').sort_values(by='user_id',
ascending=False)
b_whirpool.columns = ['b_users_count']
b_whirpool
| b_users_count | |
|---|---|
| event_name | |
| login | 450 |
| product_page | 253 |
| product_cart | 125 |
| purchase | 124 |
fig = go.Figure(go.Funnel(y=b_whirpool.index,
x=b_whirpool['b_users_count']
)
)
fig.update_layout(title="График воронки событий исследуемой группы")
fig.show()
whirpool = a_whirpool.merge(b_whirpool, on='event_name')
whirpool
| a_users_count | b_users_count | |
|---|---|---|
| event_name | ||
| login | 969 | 450 |
| product_page | 623 | 253 |
| product_cart | 300 | 125 |
| purchase | 286 | 124 |
participants_filtered.query('group == "A"')['user_id'].nunique()
1743
participants_filtered.query('group == "B"')['user_id'].nunique()
1292
Агрегированные значения совершенных действий пользователями приведены в таблице.
Значения конверсии перехода между этапами для обеих групп близки между собой.
Однако, принимая во внимание исходные размеры выборок заметно, что конверсия пользователей группы B на этапе login заметно н
При проведении A/B тестирования необходимо обращать внимание на следующие параметры:
При подготовке и проведении исследования был допущен ряд неточностей, которые могли повлиять на результаты исследования:
Также стоит обратить внимание на то, что показатель конверсии на первом шаге у группы B оказался ниже, чем у контрольной группы, но выше на втором.
Следует проверить, насколько значимы данные различия.
Формулируются следующие нулевая и альтернативная гипотезы:
Для каждого из этапов:
H0: Конверсия пользователей групп A и B не отличается
H1: Имеется значимое различие конверсии пользователей групп A и B
participants_filtered.query('group == "A"')['user_id'].nunique()
1743
participants_filtered.query('group == "B"')['user_id'].nunique()
1292
whirpool['a_users_count'][0]
969
whirpool['b_users_count'][0]
450
def z_test(event, alpha=0.05):
trials1 = participants_filtered.query('group == "A"')['user_id'].nunique()
trials2 = participants_filtered.query('group == "B"')['user_id'].nunique()
successes1 = whirpool['a_users_count'][event]
successes2 = whirpool['b_users_count'][event]
print(trials1)
print(trials2)
print(successes1)
print(successes2)
z_test(whirpool.index[0], alpha=0.05 / 4)
whirpool['a_users_count'] = round(whirpool['a_users_count'] * 100 / \
participants_filtered.query('group == "A"')['user_id'].nunique(), 2)
whirpool['b_users_count'] = round(whirpool['b_users_count'] * 100 / \
participants_filtered.query('group == "B"')['user_id'].nunique(), 2)
whirpool.columns = ['a_total_ratio', 'b_total_ratio']
whirpool
plt.figure(figsize=(10, 5))
sns.lineplot(x=whirpool.index,
y=whirpool['a_total_ratio'],
label="A"
)
sns.lineplot(x=whirpool.index,
y=whirpool['b_total_ratio'],
label="B"
)
plt.title('Конверсия пользователей на различных этапах')
plt.ylabel('Общая конверсия, %')
plt.xlabel('Этап');
На каждом из этапов заметно существенное отличие конверсии между группами.
Группа B, с обновленной рекомендательной системой показывает более низкие показатели.
На проведение данного тестирования оказал влияние ряд факторов, такие как сезонность, пересечение с маркетинговыми событиями. Также при наборе данных были неточности, которые были отфильтровано, что снизило размер выборок.
Тем не менее, результат сравнения конверсии выявил статистически значимую разницу для каждого совершаемого действия.
Пользователи группы B, которые использовали новую рекомендательную систему совершали целевые действия в среднем на 40% реже, что свидетельствует о неудовлетворенности пользователей данной системой.
В контрольной группе наблюдался заметный рост количества пользовательских действий в течение определенного времени.
В то же время для пользователей группы B данный эффект проявился в гораздо меньшей степени.
Помимо этого, на протяжении всего теста пользователи группы B демонстрируют вдвое меньший показатель конверсии.
Результат теста можно считать статистически значимым и при необходимости, повести его повторно после доработки рекомендательной системы, с учетом корректировок распределения пользовательского трафика и длительности проведения теста.